package com.javaspeak.concurrency.lock.custom.reentrant;

import java.util.Random;

import com.javaspeak.concurrency.lock.custom.reentrant.ReEntrantLock;

/**
 * This class uses the ReEntrant Lock.
 * <p>
 * The idea behind a reentrant lock is that a thread that has taken the lock
 * can take the lock again without it blocking for that thread.  Other threads
 * will of course have to wait until the thread which has the lock has released
 * the lock.
 * <p>
 * The renentrant implementation in the ReEntrant class keeps a tab on how
 * many time the thread has acquired the lock before releasing it.
 * Only when the thread holding the lock has called releaseLock(..) the
 * same number of times as it called aquireLock(..) will it release the
 * lock on the monitor and let other threads take the lock.
 * <p>
 * The renentrant implementation will only allow a thread to take the lock a
 * second time if it is the thread already holding the lock.  It checks which
 * thread has the locks using:
 * <p>
 * Thread currentThread = Thread.currentThread();
 *
 * @author John Dickerson
 */
public class Counter {

    private ReEntrantLock lock = new ReEntrantLock();
    private int count = 0;
    private Random random = new Random();


    private int increment( int timeOutInMilliseconds )
        throws InterruptedException{

        if ( lock.acquireLock( timeOutInMilliseconds ) ){

            try {
                count++;
            }
            finally {
                lock.releaseLock();
            }

            return count;
        }
        else {

            throw new RuntimeException( "Could not acquire lock" );
        }
    }


    public int getDoubleIncrement( int timeOutInMilliseconds )
        throws InterruptedException {

        if ( lock.acquireLock( timeOutInMilliseconds ) ){

            try {
                int randomInt = random.nextInt( 99 ) * 10;

                Thread.sleep( randomInt );

                // We have already acquired the lock above. We are about to call
                // the increment(..) method which will again call the
                // lock.acquireLock method.  If the code does not hang
                // that means the reentrant part of the lock is probably
                // working.
                increment( timeOutInMilliseconds );

                count++;
            }
            finally {
                lock.releaseLock();
            }

            return count;
        }
        else {

            throw new RuntimeException( "Could not acquire lock" );
        }
    }
}
